package problemloesen.projekt01;

import java.util.Random;

public class PointApprox extends Point {
	
	/**
	 * static values for g(x) we want to approx. Calculate once at the
	 * beginning. Example: Step, (X, Y) 
	 * 0 ( 0.1, 0.92), 1 ( 0.2, 0.87), 2 ( 0.3, 0.85), ...
	 */
	private static double valuesToApprox[][];
	private static Function func;
	
	private double [][]approxvalues;
	private double [][]failure;
	
	private Random rnd;
	private final double GAUSS_MULT = 1.0;

	public Function getFunc() {
		return func;
	}
	
	public PointApprox ()
	{
	}
	
	public PointApprox (double[] values)
	{
		this.values = values;
	}

	public double[][] getApproxvalues() {
		return approxvalues;
	}

	public void setApproxvalues(double[][] approxvalues) {
		this.approxvalues = approxvalues;
	}

	public double[][] getFailure() {
		return failure;
	}

	public void setFailure(double[][] failure) {
		this.failure = failure;
	}

	public static double[][] getValuesToApprox() {
		return valuesToApprox;
	}

	public static void setValuesToApprox(double[][] valuesToApprox) {
		PointApprox.valuesToApprox = valuesToApprox;
	}

	/**
	 * Calculates polynom with coefficts a[a0, a1, a2 ...] for one given x
	 * 
	 * @param x
	 * @param a
	 * @return f(x)
	 */
	public static double poly(double x, double[] a) {
		double res = 0;
		for (int i = a.length - 1; i > 0; i--) {
			res += a[i];
			res *= x;
		}
		res += a[0];
		return res;
	}

	/**
	 * Call this once to calculate valuesToApprox for given function from
	 * intervalStart to intervalEnd with number of steps
	 * 
	 * @param intervalStart
	 *            begin at
	 * @param intervalEnd
	 *            end at
	 * @param numberOfSteps
	 *            number of steps to do
	 * 
	 */
	public static double[][] calculateToApprox_for_interval(double intervalStart,
			double intervalEnd, int numberOfSteps) {
		valuesToApprox = new double[numberOfSteps + 1][2];
		int i = 0;
		double stepSize = ((intervalEnd - intervalStart) / numberOfSteps);

		// for all steps for valuesToApprox
		for (double counter = intervalStart; counter < intervalEnd; counter += stepSize) {
			valuesToApprox[i][1] = calcToApprox(counter); // save y
			valuesToApprox[i][0] = counter; // save x
			i++;
		}
	return valuesToApprox;
	}
	


	/**
	 * g(x) we want to approximate
	 * 
	 * @param x		X-Value
	 * @return g(x) Y-Value
	 */
	private static double calcToApprox(double x) {
//		if (func != null)
//		{
//			return func.function(x);
//		}
//		else 
//		{
//			return Math.sin(x) / x;
//		}
		// auskommentiert weil zu langsam 
		//return Math.sin(x) / x;
		return Math.cos(x);
		
	}

	/**
	 * Evaluates given list of Points and returns actual maximum difference from
	 * target
	 * 
	 * @return Maximum difference for target
	 */
	public double eval() {
		double max = 0; // max. difference
		// for all elements in vlauesToApprox
		for (int i = 0; i < valuesToApprox.length; i++) {
			// currentDiff = |g(x) - f(x)| at interval step i
			double currentDiff = Math.abs(valuesToApprox[i][1]
					- poly(valuesToApprox[i][0], this.values));
			// if difference is > current max difference
			if (currentDiff > max) {
				// remember biggest max
				max = currentDiff;
			}
		}
		return max;
	}
	
	
	
	/**
	 * Calculate failure for each point in values for approx
	 * 
	 * 
	 * @param values
	 *            list of values for evaluation
	 * @return failure for each element in
	 */
	public static double[][] calculateError(double[] values) {
		double failure[][] = new double[valuesToApprox.length][2];
		for (int i = 0; i < valuesToApprox.length; i++) {
			failure[i][1] = valuesToApprox[i][1]
					- poly(valuesToApprox[i][0], values);
			failure[i][0] = valuesToApprox[i][0];
		}
		return failure;
	}
	
	/**
	 * Calculate failure for each point in values for approx
	 * 
	 * 
	 * @param values
	 *            list of values for evaluation
	 * @return final values for each element in
	 */
	public static double[][] calculateFinalPoly(double[] values) {
		double finalpoly[][] = new double[valuesToApprox.length][2];
		for (int i = 0; i < valuesToApprox.length; i++) {
			finalpoly[i][1] = poly(valuesToApprox[i][0], values);
			finalpoly[i][0] = valuesToApprox[i][0];
		}
		return finalpoly;
	}
	
	public static void setFunc(Function func) {
		PointApprox.func = func;
	}
	
	public Point clone()
	{
		return new PointApprox(this.values.clone());
	}
	
	public void allZero()
	{
		for (int i = 0; i < values.length; i++) {
			values[i] = 0;
		}
	}
	
	public Point gaussianClone()
	{
		double []newValues = new double[this.values.length];
		for (int i = 0; i<newValues.length;i++)
		{
			if (rnd == null) rnd = new Random(System.currentTimeMillis());
			newValues[i] += (rnd.nextGaussian() * GAUSS_MULT);
		}
		return new PointApprox(newValues);
	}
	
}
